const NODE_ENV = process.env.NODE_ENV = 'development';
const path = require('path');
const spawn = require('child_process');
const chokidar = require('chokidar');
const webpack = require('webpack');
const webpackDevMiddleware = require('webpack-dev-middleware');
const webpackHotClient = require('webpack-hot-client5');
const open = require('open');
const EVENT_CONF = require('./config/development.event');
const utils = require('./utils');
const config = require('./config');
const webpackConfig = require(`./webpack.${NODE_ENV}.conf`);
const forkServer = path.resolve(__dirname, './dev-forkserver');

function devServer() {
  const compiler = webpack(webpackConfig);
  // 这里不启动服务，故而不需要webpackDevMiddleware，但是需要其将转换成内存的功能
  const devMiddleweare = webpackDevMiddleware(compiler, {
    stats: {
      colors: true
    },
    publicPath: config.dev.publicPath, // 主要这里web服务监听目录不要和后端目录监听有重，否则会出现堆栈溢出
  });

  const { server: wss } = webpackHotClient(compiler, {
    hmr: true
  });

  let restartCount = 0, worker;
  const watchConf = {
    persistent: true,
    ignored: [utils.resolve('src/static')],
    ignoreInitial: true,
    usePolling: true
  };
  compiler.hooks.done.tap('WebpackNodeServer', stats => {
    // 第一次开启监听
    worker && worker.kill('SIGINT');
    worker = spawn.fork(forkServer);
    workerListener(worker, wss, compiler, async msg => {
      if (!restartCount) {
        // 开启阅览
        await open(`http://localhost:${msg.port}`);
      }
      restartCount++
    });
  });

  // 监听服文件变化但是排除客户端webpack监听文件
  chokidar.watch(utils.resolve('src'), watchConf).on('change', filePath => {
    // console.log(`文件: ${filePath}发生了改变!`);
    if (!worker) {
      return;
    }
    worker.kill('SIGINT');
    worker = spawn.fork(forkServer);
    workerListener(worker, wss, compiler);
  });
}

function workerListener(worker, wss, compiler, cb) {
  // 获得消息重启服务
  worker.on('message', msg => {
    switch (msg.action) {
      case EVENT_CONF.EVENT_FILE_READ: {
        const fileName = msg.fileName;
        const key = msg.key;
        let content = '';
        const filePath = path.join(compiler.outputPath, fileName);
        let action = EVENT_CONF.EVENT_FILE_READ_OK;
        try {
          content = compiler.outputFileSystem.readFileSync(filePath, EVENT_CONF.CHARSET).toString();
          action = EVENT_CONF.EVENT_FILE_READ_OK;
        } catch (err) {
          content = err;
          action = EVENT_CONF.EVENT_FILE_READ_Failed;
        }
        worker.send({
          action,
          fileName,
          content,
          key
        });
        break;
      }
      case EVENT_CONF.EVENT_RELOAD: {
        wss.broadcast(JSON.stringify({
          type: 'window-reload'
        }));

        typeof cb === 'function' && cb(msg);
      }
      default:
        break;
    }
  });
}
module.exports = devServer;
